/**
 * 正则工具类
 * 常用正则表达式放在 regular.json 配置文件里，
 * 如手机号验证规则，需要验证手机号码有效性时，
 * 只需要传入手机号和 mobile 关键词，即可返回验证结果。
 * 
 * @author lms 2016.07.05
 * last update by lms 2021.08.02 加载正则配置可指定
 * 使用方法:
 * 1、调用 loadRegularConfigFile 指定正则配置文件位置；
 * 2、如果步骤1不指定正则配置文件位置，工具默认加载 resources目录下的 /config/regular.json 文件作为正则配置文件
 */
package ms.core.tool;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegularTool {
	private static boolean inited    = false;
	private static Object regularObj = null;
	private static String configFile = "/config/regular.json";	//默认regular.json配置文件

	public static void loadRegularFile(String regularFile) {
		try {
			if (inited) return;

			InputStream is = StreamTool.loadFileToStream(RegularTool.class, regularFile);
			String txt = SysTool.readTxtFile(is, "UTF-8");
			regularObj = JsonTool.getJSONObject(txt);
			inited = true;
		} catch (Exception e) {
			Log4j2Tool.error(RegularTool.class, "loadRegularFile:", e);
		}
	}

	private static boolean match(String regex, String str) {
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(str);
		return matcher.matches();
	}
	
	/**
	 * 验证正则配置文件指定key节点值是否匹配规则
	 * @param key 正则配置文件的key
	 * @param value 校验的值
	 * @return
	 */
	public static CheckResult checkValue(String key, String value){
		loadRegularFile(configFile);

		CheckResult ret = new CheckResult();
		ret.setResult(false);
		Object node = JsonTool.getValue(regularObj, key);
		if (node==null){
			ret.setMsg("未配置"+key+"验证表达式！");
			return ret;
		}
		//to validate value is null
		if (StrTool.isBlank(value)){
			ret.setMsg("请输入"+JsonTool.getStrValue(node, "desc", null));
			return ret;
		}
		//to validate value's format
		String regEx = JsonTool.getStrValue(node, "reg", value);
		ret.setResult(match(regEx, value));
		if (ret.getResult())
			ret.setMsg("验证通过");
		else
			ret.setMsg(JsonTool.getStrValue(node, "desc", null)+"格式错误");
		return ret;
	}

	/**
	 * 按json格式校验数据
	 * @param json
	 * @return
	 */
	public static CheckResult checkValue(String json){
		loadRegularFile(configFile);

		CheckResult ret = new CheckResult();
		ret.setResult(false);
		Map<String, Object> objValids = JsonTool.objectToMap(json, true);
		if (objValids==null) {
			ret.setMsg("校验对象是无效json！");
			return ret;
		}

		for(Map.Entry<String, Object>item: objValids.entrySet()) {
			String key   = item.getKey();
			//判断某key是否需要校验
			Object regular = JsonTool.getValue(regularObj, key);
			if (regular==null) continue;

			if (item.getValue()==null) {
				ret.setMsg("请输入"+JsonTool.getStrValue(regular, "desc", null));
				return ret;
			}

			String regEx = JsonTool.getStrValue(regular, "reg", null);
			ret.setResult(match(regEx, item.getValue().toString()));
			if (!ret.getResult()) {
				ret.setMsg(JsonTool.getStrValue(regular, "desc", null)+"格式错误");
				return ret;
			}
		}
		ret.setResult(true);
		ret.setMsg("验证通过！");
		return ret;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * to get matched string from content by regEx regular express
	 * @param content is the source of to analysis text
	 * @param regEx is regular express
	 * @param goupId means you need to use the group regular, like (.+?)<a, it indicate the group index
	 * @param loop to decide to loop analysis
	 * @return empty string if not matched or return matched string
	*/
	public static String getMatchGroupText(String content, String regEx, int groupId, boolean loop){
		if (StrTool.isBlank(regEx) || StrTool.isBlank(content))
			return "";

		String ret = "";
		try{
			Pattern p = Pattern.compile(regEx);
			Matcher m = p.matcher(content);
			if (loop){
				while (m.find()){
					ret = ret+m.group(groupId).trim()+"\n";
				}
			}else{
				if (m.find()){
				ret = m.group(groupId).trim();
				}
			}
		}catch(Exception e){
			Log4j2Tool.error(RegularTool.class, "getMatchGroupText:", e);
		}
		return ret;
	}

	/**
	 * to get matched string from content by regEx regular express
	 * 
	 * @param content is the source of to analysis text
	 * @param regEx is regular express
	 * @return null if not matched or return matched string
	*/
	public static List<String[]> getMatchGroupText(String content, String regEx){
		if (StrTool.isBlank(regEx) || StrTool.isBlank(content))
			return null;

		List<String[]> rets = new ArrayList<>();
		try{
			Pattern p = Pattern.compile(regEx);
			Matcher m = p.matcher(content);
			while (m.find()){
				String[] items = new String[m.groupCount()];
				for(int i=1; i<=m.groupCount(); i++)
					items[i-1] = m.group(i).trim();
				rets.add(items);
			}
		}catch(Exception e){
			Log4j2Tool.error(RegularTool.class, "getMatchGroupText:", e);
		}
		return rets;
	}

	/**
	 * to analysis matched string from content by regEx regular express
	 * @param content content of to analysis
	 * @param regEx regular express of to fetch the content
	 * @param loop decide to match for all text
	 * @return empty string if failure or return the matched text
	 */
	public static String getMatchText(String content, String regEx, boolean loop){
		if (StrTool.isBlank(regEx) || StrTool.isBlank(content))
			return "";

		String ret = "";
		try{
			Pattern p = Pattern.compile(regEx);
			Matcher m = p.matcher(content);
			if (loop){
				while (m.find())
					ret = ret+m.group().trim()+System.lineSeparator();
			}else{
				if (m.find()){
					ret = m.group().trim();
				}
			}
		}catch(Exception e){
			Log4j2Tool.error(RegularTool.class, "getMatchText:", e);
		}
		return ret;
	}
	
	/**
	 * split string by regular express
	 * @param content
	 * @param regEx
	 * @return null or List<String>
	 */
	public static List<String> splitText(String content, String regEx){
		String[] arys = content.split(regEx);
		if (arys==null || arys.length==0) return null;

		return Arrays.asList(arys);
	}

    /**
     * to verify password security rank
     * @param str key to verify
     * @return 0-not match;1-simple;2-middle;3-super
     */
	public static int keySecurityRank(String str){
		if ((str==null) || (str.length()<6))
			return 0;
		
		String simple = "^(?:\\d+|[a-zA-Z]+|[!@#$%^&*]+)$";
		String middle = "^(?![a-zA-z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[a-zA-Z\\d!@#$%^&*]+$";
		String high = "[\\p{Punct}]+";
		//simple
		Pattern p = Pattern.compile(simple);
		Matcher m = p.matcher(str);
		if (m.find())
			return 1;
		//middle
		p = Pattern.compile(middle);
		m = p.matcher(str);
		if (m.find())
			return 2;
		//super
		p = Pattern.compile(high);
		m = p.matcher(str);
		if (m.find())
			return 3;

		return 4;
	}
	
	public static class CheckResult {
		//private declarations
		private boolean result;
		private String msg;

		public void setResult(boolean result) { this.result = result; }
		public boolean getResult() { return this.result; }

		public String getMsg() { return msg; }
		public void setMsg(String msg) { this.msg = msg; }
	}
}
